package 反射;

//Field 提供有关类或接口的单个字段的信息，以及对它的动态访问权限。反射的字段可能是一个类（静态）字段或实例字段。
//
//《Field 成员变量的介绍》
//每个成员变量有类型和值。
//
//java.lang.reflect.Field 为我们提供了获取当前对象的成员变量的类型，和重新设值的方法。
//
//《获取变量的类型》
//类中的变量分为两种类型：基本类型和引用类型：
//
//-基本类型（ 8 种）
//  -整数：byte, short, int, long
//  -浮点数：float, double
//  -字符：char
//  -布尔值：boolean
//
//-引用类型
//  -所有的引用类型都继承自 java.lang.Object
//  -类，枚举，数组，接口都是引用类型
//  -java.io.Serializable 接口，基本类型的包装类（比如 java.lang.Double）也是引用类型
//
//java.lang.reflect.Field 提供了两个方法获去变量的类型：
// -Field.getType()：返回这个变量的类型
// -Field.getGenericType()：如果当前属性有签名属性类型就返回，否则就返回 Field.getType()
//
//Class<?> getType()           返回一个 Class 对象，它标识了此 Field 对象所表示字段的声明类型。
//Type getGenericType()        返回一个 Type 对象，它表示此 Field 对象所表示字段的声明类型。 
//
//实例：
//import java.lang.reflect.Modifier;
//import java.lang.reflect.Field;
//
//class A {
//	public String id;
//	protected String name;
//	int age;
//	private String sex;
//	int[][] ints;
//}

//public class 访问字段1{	
//	/**
//	 * 返回该类所在包的包名字字符串
//	 * @param thisClass 一个类的Class对象
//	 * @return 该类的包名字字符串
//	 */
//	public static String getThisPackageName(Class<?> thisClass)	{
//		String thisClassName=thisClass.getName();
//		String thispackage=thisClassName.substring(0,thisClassName.lastIndexOf("."));
//		return thispackage;
//	}
//	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException{
//		Class strClass=Class.forName(getThisPackageName(访问字段1.class)+".A");
//		//获取类的所有声明的字段
//		Field[] sField=strClass.getDeclaredFields();
//		for (Field field : sField){
//			//获取字段的名字
//			System.out.printf("Field：%-5s|",field.getName());
//			//获取字段的类型的Class类，然后获取规范化的名字
//			System.out.printf("Type：%-18s|",field.getType().getCanonicalName());
//			//获取字段的类型的Type类对象，然后获取类的名字
//			System.out.printf("GenericType:%-18s|",field.getGenericType().getTypeName());
//			System.out.println();
//		}
//	}
//}

//Field：id  |Type：java.lang.String  |GenericType:java.lang.String  |
//Field：name|Type：java.lang.String  |GenericType:java.lang.String  |
//Field：age |Type：int               |GenericType:int               |
//Field：sex |Type：java.lang.String  |GenericType:java.lang.String  |
//Field：ints|Type：int[][]           |GenericType:int[][]           |
//
//可以看到这个两个方法都能正确的返回当前字段的类型。这两个方法的区别还是在返回类型上
//
//getType()方法返回的是Class<?>类型的，而getGenericType()返回的是Type类型的。
//
//《获取成员变量的修饰符》
//成员变量可以被以下修饰符修饰：
//
//访问权限控制符：public, protected, private
//限制只能有一个实例的：static
//不允许修改的：final
//不会被序列化：transient
//线程共享数据的一致性：volatile
//注解
//类似获取 Class 的修饰符，我们可以使用 Field.getModifiers() 方法获取当前成员变量的修饰符。
//返回 java.lang.reflect.Modifier 中定义的整形值。然后使用 Modifier.toString(int mod)解码成字符串
//
//实例：获取上面的A类的字段的类型和修饰符：
//
//import java.lang.reflect.Modifier;
//import java.lang.reflect.Field;
//
//class A {
//	public String id;
//	protected String name;
//	int age;
//	private String sex;
//	int[][] ints;
//}
//public class 访问字段1 {
//	public static void printField(Class<?> class1) {
//		// 获取类的所有声明的字段
//		Field[] sField = class1.getDeclaredFields();
//		for (Field field : sField) {
//			// 获取字段的名字
//			System.out.printf("字段：%-4s|", field.getName());
//			// 获取字段的类型的Class类，然后获取规范化的名字
//			System.out.printf("类型：%-18s|", field.getGenericType().getTypeName());
//			// 使用Field.getModifiers()，可获取字段的修饰符编码，
//			// 然后再使用Modifier.toString(int code)，来解码成字字符串
//			System.out.printf("修饰符：%s", Modifier.toString(field.getModifiers()));
//			System.out.println();
//		}
//	}
//
//	public static String getThisPackageName(Class<?> thisClass) {
//		String thisClassName = thisClass.getName();
//		String thispackage = thisClassName.substring(0, thisClassName.lastIndexOf("."));
//		return thispackage;
//	}
//
//	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
//		Class AClass = Class.forName(getThisPackageName(访问字段1.class) + ".A");
//		printField(AClass);
//	}
//}
//运行结果：
//
//字段：id  |类型：java.lang.String  |修饰符：public
//字段：name|类型：java.lang.String  |修饰符：protected
//字段：age |类型：int               |修饰符：
//字段：sex |类型：java.lang.String  |修饰符：private
//字段：ints|类型：int[][]           |修饰符：

//由于 Field 间接继承了 java.lang.reflect.AnnotatedElement ，因此运行时也可以获得修饰成员变量的注解，当然前提是这个注解
//被 java.lang.annotation.RetentionPolicy.RUNTIME 修饰。
//《获取和修改成员变量的值》
//拿到一个对象后，我们可以在运行时修改它的成员变量的值，对运行时来说，反射修改变量值的操作和类中修改变量的结果是一样的。
//
//1.基本类型的获取方法：
//byte getByte(Object obj) 
//          获取一个静态或实例 byte 字段的值。 
//int getInt(Object obj) 
//          获取 int 类型或另一个通过扩展转换可以转换为 int 类型的基本类型的静态或实例字段的值。 
// short getShort(Object obj) 
//          获取 short 类型或另一个通过扩展转换可以转换为 short 类型的基本类型的静态或实例字段的值。 
// long getLong(Object obj) 
//          获取 long 类型或另一个通过扩展转换可以转换为 long 类型的基本类型的静态或实例字段的值。 
// float getFloat(Object obj) 
//          获取 float 类型或另一个通过扩展转换可以转换为 float 类型的基本类型的静态或实例字段的值。 
// double getDouble(Object obj) 
//          获取 double 类型或另一个通过扩展转换可以转换为 double 类型的基本类型的静态或实例字段的值。 
// boolean getBoolean(Object obj) 
//          获取一个静态或实例 boolean 字段的值。 
// char getChar(Object obj) 
//          获取 char 类型或另一个通过扩展转换可以转换为 char 类型的基本类型的静态或实例字段的值。
//2.基本类型的setter方法：
//
// void setByte(Object obj, byte b) 
//          将字段的值设置为指定对象上的一个 byte 值。 
// void setShort(Object obj, short s) 
//  将字段的值设置为指定对象上的一个 short 值。
// void setInt(Object obj, int i) 
//          将字段的值设置为指定对象上的一个 int 值。 
// void setLong(Object obj, long l) 
//          将字段的值设置为指定对象上的一个 long 值。 
// void setFloat(Object obj, float f) 
//          将字段的值设置为指定对象上的一个 float 值。
// void setDouble(Object obj, double d) 
//          将字段的值设置为指定对象上的一个 double 值。
// void setBoolean(Object obj, boolean z) 
//          将字段的值设置为指定对象上的一个 boolean 值。 
// void setChar(Object obj, char c) 
//          将字段的值设置为指定对象上的一个 char 值。
//3.引用类型的getters方法：
//
//Object get(Object obj) 
//          返回指定对象上此 Field 表示的字段的值。 
//4.引用类型的setters方法：
//
//void set(Object obj, Object value) 
//          将指定对象变量上此 Field 对象表示的字段设置为指定的新值。 
//实例：
//
//(1)基本类型的获取和修改：
//import java.lang.reflect.Field;
//class C{
//	private int a;
//	private double d;
//	private boolean flag;
//	@Override
//	public String toString(){
//		return "C [a=" + a + ", d=" + d + ", flag=" + flag + "]";
//	}
//	public C(int a, double d, boolean flag){
//		super();
//		this.a = a;
//		this.d = d;
//		this.flag = flag;
//	}
////	public int getA(){
////		return a;
////	}
////	public void setA(int a){
////		this.a = a;
////	}
////	public double getD(){
////		return d;
////	}
////	public void setD(double d){
////		this.d = d;
////	}
////	public boolean isFlag(){
////		return flag;
////	}
////	public void setFlag(boolean flag){
////		this.flag = flag;
////	}
//}
//
//public class 访问字段1{
//	public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException{
//		C c = new C(10, 123.456, true);
//		System.out.println("c对象的值：");
//		System.out.println(c);
//		System.out.println("-------------------------------");
//		Class cClass = c.getClass();
//		// 获取所有的字段
//		Field[] cFields = cClass.getDeclaredFields();
//		for (Field field : cFields){
//			field.setAccessible(true);
//			System.out.println("获取到字段：" + field.getType().getCanonicalName()+ ",值：" + field.get(c));
//		}
//		for (Field field : cFields){
//			if (field.getType().getCanonicalName() == "int"){
//				field.setInt(c, 30);
//			} else if (field.getType().getCanonicalName() == "double"){
//				field.setDouble(c, 6789.9901);
//			} else if (field.getType().getCanonicalName() == "boolean"){
//				field.setBoolean(c, false);
//			}
//			// System.out.println(field.getType().getCanonicalName());
//		}
//		System.out.println("-------------------------------");
//		System.out.println("现在的c对象的值：");
//		System.out.println(c);
//	}
//}
//运行结果：
//
//c对象的值：
//C [a=10, d=123.456, flag=true]
//-------------------------------
//获取到字段：int,值：10
//获取到字段：double,值：123.456
//获取到字段：boolean,值：true
//-------------------------------
//现在的c对象的值：
//C [a=30, d=6789.9901, flag=false]


//(2)引用类型的获取和修改
//
//测试类：
//import java.lang.reflect.Field;
//class B{
//	private String id;
//	private String Name;
//	public B(String id, String name){
//		super();
//		this.id = id;
//		Name = name;
//	}
//	public B(){}
//	@Override
//	public String toString(){
//		return "B [id=" + id + ", Name=" + Name + "]";
//	}
//	public String getId(){
//		return id;
//	}
//	public void setId(String id){
//		this.id = id;
//	}
//	public String getName(){
//		return Name;
//	}
//	public void setName(String name){
//		Name = name;
//	}
//}
////这里测试的类B的字段都是private类型的，在外部类是无法直接访问到这些成员属性的，想要获取和修改只能通过B类的getters和setters方法进行。使用反射我可以
////绕过这些限制，因为是私有类型的要使用 Field.setAccessible(true); 方法解除限制
//
//public class 访问字段1{
//    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException{
//        B b=new B("B1000","小明");
//        System.out.println("b对象的值：");
//        System.out.println(b);
//        System.out.println("---------------------------------");
//        Class bClass=b.getClass();
//        //获取共有的字段列表
//        Field[] bFields=bClass.getDeclaredFields();
//        System.out.println("通过反射获取字段的值：");
//        //获取字段的值，Field.get(对象);
//        for (Field field : bFields){
//            field.setAccessible(true);//获取权限
//            //获取b对象的字段field里面的值
//            System.out.println("字段："+field.getType().getName()+",值："+field.get(b));
//        }
//        //修改字段的值
//        for (Field field : bFields){
//            field.set(b, "哈哈");
//        }
//        System.out.println("---------------------------------");
//        System.out.println("现在的b对象的值：");
//        System.out.println(b);
//    }
//}
 
// 
//运行结果：
//
//b对象的值：
//B [id=B1000, Name=小明]
//---------------------------------
//通过反射获取字段的值：
//字段：java.lang.String,值：B1000
//字段：java.lang.String,值：小明
//---------------------------------
//现在的b对象的值：
//B [id=哈哈, Name=哈哈]
//再说一下setAccessible()方法，Field的setAccessible()方法是从AccessibleObject类继承而来的。AccessibleObject 类
//是 Field、Method 和 Constructor 对象的基类。
//它提供了在使用时取消默认 Java 语言访问控制检查的能力。
//
//一般情况下，我们并不能对类的私有字段进行操作，利用反射也不例外，但有的时候，例如要序列化的时候，我们又必须有能力去处理这些字段，这时候，我
//们就需要调用AccessibleObject上的setAccessible()方法来允许这种访问，而由于反射类中的Field，Method和Constructor继承自AccessibleObject，
//因此，通过在Field，Method和Constructor这些类上调用setAccessible()方法，我们可以操作这些字段无法访问的字段。
//
//返回boolean的方法：
//
//boolean equals(Object obj) 
//          将此 Field 与指定对象比较。 
//boolean isEnumConstant() 
//          如果此字段表示枚举类型的元素，则返回 true；否则返回 false。 
//boolean isSynthetic() 
//          如果此字段是复合字段，则返回 true；否则返回 false。

//返回String的方法：
//
//String getName() 
//         返回此 Field 对象表示的字段的名称。 
//String toGenericString() 
//         返回一个描述此 Field（包括其一般类型）的字符串。 
//String toString() 
//         返回一个描述此 Field 的字符串。 
//其他方法：
//
//1.equals()和hashCode()
//
//int hashCode()
//          返回该 Field 的哈希码。
//boolean equals(Object obj)
//          将此 Field 与指定对象比较。 

//2.返回注释的方法：
//
//<T extends Annotation> T  getAnnotation(Class<T> annotationClass)
//          如果存在该元素的指定类型的注释，则返回这些注释，否则返回 null。
//Annotation[] getDeclaredAnnotations()
//          返回直接存在于此元素上的所有注释。 

//3.返回字段所在的类或者接口的Class对象
//
// Class<?> getDeclaringClass()
//          返回表示类或接口的 Class 对象，该类或接口声明由此 Field 对象表示的字段。 

//4.返回字段的类型（Type）
//
//Type getGenericType()
//          返回一个 Type 对象，它表示此 Field 对象所表示字段的声明类型。 

//5.返回修饰符编码：这个方法上面已经提到了,可以使用Modifier.toString(int mod)方法，把获取到的编码转换成修饰符字符串
//
// int getModifiers()
//          以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符。 
//常见错误 1 ：无法转换类型导致的 java.lang.IllegalArgumentException
//    在使用反射获取或者修改一个变量的值时，编译器不会进行自动装/拆箱。所以我们无法给 Integer 类型的属性使用 setInt() 方法重新设值，必须给它
//    赋一个 Integer 对象才可以。
//    否则会因为无法转换类型而出现java.lang.IllegalArgumentException
//
//实例：
//
import java.lang.reflect.Field;
public class 访问字段1{
    private Integer integer;
    public 访问字段1(Integer integer){
        this.integer = integer;
    }
    public String toString(){
        return "TestInterger [integer=" + integer + "]";
    }
    public static void main(String[] args) throws NoSuchFieldException,SecurityException, IllegalArgumentException, IllegalAccessException{
        // 这里传入的30是int类型的，会自动装箱成Integer类型
    	访问字段1 testInterger = new 访问字段1(30);
        System.out.println("testInteger对象：");
        System.out.println(testInterger);
        System.out.println("----------------------------------");
        Class thisClass = testInterger.getClass();
        // 获取integer字段
        Field inField = thisClass.getDeclaredField("integer");
        inField.setAccessible(true);// 不做访问控制检查
        // 获取字段的值
        System.out.println("成员属性：" + inField.getType().getCanonicalName()+ "，值：" + inField.get(testInterger));
        // 修改成员属性integer:
//        inField.setInt(testInterger, 90);//报异常
//        inField.set(testInterger, Integer.valueOf(90));
        inField.set(testInterger, 90);

        System.out.println("----------------------------------");
        System.out.println(testInterger);
    }
}
//运行结果：
//
//testInteger对象：
//TestInterger [integer=30]
//----------------------------------
//成员属性：java.lang.Integer，值：30
//Exception in thread "main" java.lang.IllegalArgumentException: 
//Can not set java.lang.Integer field reflect.fieldtest.type.TestInterger.integer to (int)90
//	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
//	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
//	at sun.reflect.UnsafeObjectFieldAccessorImpl.setInt(Unknown Source)
//	at java.lang.reflect.Field.setInt(Unknown Source)
//	at reflect.fieldtest.type.TestInterger.main(TestInterger.java:34)
//解决方法：把上面的 inField.setInt(testInterger, 90);改成 inField.set(testInterger, 90);即可；
//
//set方法原型：
//
//Field.set(Objet obj2,Object obj2)，这样我们传入90这个int类型的数据时，在编译阶段编译器会自动进行装箱等同于
//
//inField.set(testInterger, new Integer(90))。这样运行时，获取到的是Integer类型的，能正常的传入。
//
//这里再来说一下自动拆箱装箱的事情：
//
//自动装箱是java编译器在java原生类型和对应的对象包装类型上做的自动转换。
//例如，把int 装换成 Integer double转换成Double等等。如果是反过来转换，那么叫做自动拆箱，也是编译器为我们做的事情。
//
//强调：自动拆箱装箱发生在编译时刻，反射时发生在程序运行时刻。
//
//为了不混淆，利用反射修改包装类的值的时候,使用set方法，并且尽量手动装箱，也就是写成下面的形式：
//
//inField.set(testInterger, new Integer(90))
//
//常见错误 2：反射非 public 的变量导致的 NoSuchFieldException
//如果你使用 Class.getField() 或者 Class.getFields() 获取非 public 的变量，编译器会报 java.lang.NoSuchFieldException 错。
//
//常见错误 3 ：修改 final类型的变量导致的 IllegalAccessException
//当你想要获取或者修改 不可修改（final）的变量时，会导致IllegalAccessException。
//
//由于 Field 继承自 AccessibleObject , 我们可以使用 AccessibleObject.setAccessible() 方法告诉安全机制，这个变量可以访问。
//
//也就是Field.setAccessible(true)。告诉安全机制当前的这字段不做访问权限检查，这样我们就能反射修改final修饰成常量了。
//
